/*
 * @(#)CodeUtils.java  1.0 2009-1-30上午11:32:52
 *
 * 上海民航华东凯亚系统集成有限公司
 * CopyRight 1999-2009 Cares, Inc. All rights reserved.
 */
package com.site.util;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.lang.ArrayUtils;
import org.springframework.util.Assert;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * <dl>
 * <dt><b>Title:</b></dt>
 * <dd>
 * 编码工具类</dd>
 * <dt><b>Description:</b></dt>
 * <dd>
 * <p>
 * 加解密、转换编码</dd>
 * </dl>
 * 
 * @author 小叶
 * @version 1.0, 2009-1-30
 * @since cares-framework-1.5-tidy
 * 
 */
public final class CodeUtils {

	/**
	 * 生成DES密钥.
	 * 
	 * @param key_byte
	 *            - seed key
	 * @param algorthm
	 *            - 缺省为"DES"
	 * @return
	 * @throws Exception
	 */
	public static final SecretKey genDESKey(byte[] key_byte, String algorthm) {
		Assert.notNull(key_byte);
		Assert.isTrue(key_byte.length > 0);
		Assert.isTrue(
				algorthm.equalsIgnoreCase(DES_ALGORITHM)
						&& key_byte.length == 16
						|| algorthm.equalsIgnoreCase(TRIPPLEDES_ALGORITHM)
						&& key_byte.length == 48, "key length error");

		if (StringUtils.isEmpty(algorthm))
			algorthm = DEFAULT_DES_ALGORITHM;
		return new SecretKeySpec(key_byte, algorthm);
	}

	/**
	 * 生成DES密钥("DES").
	 * 
	 * @param key_byte
	 *            - seed key
	 * @return
	 * @throws Exception
	 */
	public static final SecretKey genDefaultDESKey(byte[] key_byte) {
		return genDESKey(key_byte, null);
	}

	/**
	 * 根据提供商、key、算法、传输模式得到cipher.
	 * 
	 * @param provider
	 *            - 加解密类库提供商 缺省为 SunJCE
	 * @param algorthm
	 *            - 算法 缺省为 DES
	 * @param key_byte
	 *            - seed key 必填
	 * @param transferType
	 *            - 传输模式 缺省为 DES/CBC/PKCS5Padding
	 * @param iv
	 *            - 初始化向量 - 初始化向量
	 * @param cryptMode
	 *            - 加解密模式 必填
	 * @return
	 * @throws Exception
	 */
	public static final Cipher getDesCihper(String provider, String algorthm,
			byte[] key_byte, String transferType, byte iv[], int cryptMode)
			throws Exception {
		Assert.hasText(provider);
		Assert.notNull(key_byte);
		Assert.isTrue(key_byte.length > 0);
		Assert.isTrue(cryptMode == 1 || cryptMode == 2,
				"key or cryptMode error");

		Cipher cipher = null;
		SecretKey key = null;

		if (!StringUtils.isEmpty(algorthm)) {
			key = genDESKey(key_byte, algorthm);
		} else {
			key = genDefaultDESKey(key_byte);
		}
		if (!StringUtils.isEmpty(transferType)) {
			cipher = Cipher.getInstance(transferType, provider);
		} else {
			cipher = Cipher.getInstance(DEFAULT_TRANSFER_TYPE, provider);
		}
		if (iv != null && iv.length > 0) {
			cipher.init(cryptMode, key, new IvParameterSpec(iv));
		} else {
			cipher.init(cryptMode, key);
		}
		return cipher;
	}

	/**
	 * 得到 SunJCE DES/CBC/PKCS5Padding 初始化向量为空 的cipher.
	 * 
	 * @param key_byte
	 *            - seed key
	 * @param cryptMode
	 *            - 加解密模式
	 * @return
	 * @throws Exception
	 */
	public static final Cipher getDefaultDesCihper(byte[] key_byte,
			int cryptMode) throws Exception {
		return getDesCihper(DEFAULT_PROVIDER, null, key_byte, null, null,
				cryptMode);
	}

	/**
	 * 加密 des encrypt(byte[]).
	 * 
	 * @param keyAlgorthm
	 *            - key算法 缺省为 DES
	 * @param key_byte
	 *            - seed key 必填
	 * @param transferType
	 *            - 传输模式 缺省为 DES/CBC/PKCS5Padding
	 * @param src
	 *            - 所加密的内容 byte[]
	 * @param iv
	 *            - 初始化向量
	 * @return
	 * @throws Exception
	 */
	public static final byte[] desEncrypt(String keyAlgorthm, byte[] key_byte,
			String transferType, byte[] src, byte iv[]) throws Exception {
		Cipher cipher = getDesCihper(DEFAULT_PROVIDER, keyAlgorthm, key_byte,
				transferType, iv, Cipher.ENCRYPT_MODE);
		return cipher.doFinal(src);
	}

	/**
	 * 加密 des encrypt(String).
	 * 
	 * @param keyAlgorthm
	 *            - key算法 缺省为 DES
	 * @param key_byte
	 *            - seed key 必填
	 * @param transferType
	 *            - 传输模式 缺省为 DES/CBC/PKCS5Padding
	 * @param src
	 *            - 所加密的内容 String
	 * @param iv
	 *            - 初始化向量
	 * @return
	 * @throws Exception
	 */
	public static final String desEncrypt(String keyAlgorthm, byte[] key_byte,
			String transferType, String src, byte iv[]) throws Exception {
		return new String(desEncrypt(keyAlgorthm, key_byte, transferType,
				src.getBytes(), iv));
	}

	/**
	 * 解密 des decrypt(byte[]).
	 * 
	 * @param keyAlgorthm
	 *            - key算法 缺省为 DES
	 * @param key_byte
	 *            - seed key 必填
	 * @param transferType
	 *            - 传输模式 缺省为 DES/CBC/PKCS5Padding
	 * @param src
	 *            - 所解密的内容 byte[]
	 * @param iv
	 *            - 初始化向量
	 * @return
	 * @throws Exception
	 */
	public static final byte[] desDecrypt(String keyAlgorthm, byte[] key_byte,
			String transferType, byte[] src, byte iv[]) throws Exception {
		Cipher cipher = getDesCihper(DEFAULT_PROVIDER, keyAlgorthm, key_byte,
				transferType, iv, Cipher.DECRYPT_MODE);
		return cipher.doFinal(src);
	}

	/**
	 * 解密 des decrypt(String).
	 * 
	 * @param keyAlgorthm
	 *            - key算法 缺省为 DES
	 * @param key_byte
	 *            - seed key 必填
	 * @param transferType
	 *            - 传输模式 缺省为 DES/CBC/PKCS5Padding
	 * @param src
	 *            - 所解密的内容 String
	 * @param iv
	 *            - 初始化向量
	 * @return
	 * @throws Exception
	 */
	public static final String desDecrypt(String keyAlgorthm, byte[] key_byte,
			String transferType, String src, byte iv[]) throws Exception {
		return new String(desDecrypt(keyAlgorthm, key_byte, transferType,
				src.getBytes(), iv));
	}

	/**
	 * MD5 摘要计算(byte[]).
	 * 
	 * @param src
	 *            byte[]
	 * @throws Exception
	 * @return byte[] 16 bit digest
	 */
	public static final byte[] md5Digest(byte[] src) throws Exception {
		if (ArrayUtils.isEmpty(src))
			return ArrayUtils.EMPTY_BYTE_ARRAY;
		MessageDigest alg = MessageDigest.getInstance(MD5_ALGORITHM); // MD5 is
																		// 16
																		// bit
																		// coremessage
																		// digest
		return alg.digest(src);
	}

	/**
	 * MD5 摘要计算(String).
	 * 
	 * @param src
	 *            String
	 * @throws Exception
	 * @return String
	 */
	public static final String md5Digest(String src) throws Exception {
		if (StringUtils.isEmpty(src))
			return StringUtils.EMPTY;

		byte[] digest = null;
		try {
			// 取得MD5的MessageDigest
			MessageDigest md = MessageDigest.getInstance("MD5");
			// 对其进行更新
			md.update(src.getBytes());
			// MessageDigest从digest中获值
			digest = md.digest();
		} catch (NoSuchAlgorithmException e) {
			// MD5的算法抛出异常
		}

		// Digest值得输出
		StringBuffer result = new StringBuffer();
		for (int i = 0; i < digest.length; i++) {
			int x = digest[i] & 0x00FF;
			result.append(Integer.toHexString(x));
		}

		return result.toString();

		// return new String(md5Digest(src.getBytes()));
	}

	/**
	 * SHA-1 摘要计算(byte[]).
	 * 
	 * @param src
	 *            byte[]
	 * @return Exception
	 * @throws Exception
	 */
	public static final byte[] sha1Digest(byte[] src) throws Exception {
		if (ArrayUtils.isEmpty(src))
			return ArrayUtils.EMPTY_BYTE_ARRAY;
		MessageDigest alg = MessageDigest.getInstance(SHA1_ALGORITHM);
		alg.update(src);
		return alg.digest();
	}

	/**
	 * SHA-1 摘要计算(String).
	 * 
	 * @param src
	 *            String
	 * @return Exception
	 * @throws Exception
	 */
	public static final String sha1Digest(String src) throws Exception {
		if (StringUtils.isEmpty(src))
			return StringUtils.EMPTY;
		return byte2hex(CodeUtils.sha1Digest(src.getBytes()));
	}

	/**
	 * SHA-1 摘要计算(String).返回16位字符
	 * 
	 * @param src
	 *            String
	 * @return Exception
	 * @throws Exception
	 */
	public static final String sha1Digest16(String src) throws Exception {
		if (StringUtils.isEmpty(src))
			return StringUtils.EMPTY;
		return byte2hex16(CodeUtils.sha1Digest(src.getBytes()));
	}

	/**
	 * BASE64 编码(String).
	 * 
	 * @param src
	 *            String inputed string
	 * @return String returned string
	 */
	public static final String base64Encode(String src) {
		if (StringUtils.isEmpty(src))
			return StringUtils.EMPTY;
		return new BASE64Encoder().encode(src.getBytes());
	}

	/**
	 * BASE64 编码(byte[]).
	 * 
	 * @param src
	 *            byte[] inputed string
	 * @return String returned string
	 */
	public static final String base64Encode(byte[] src) {
		if (ArrayUtils.isEmpty(src))
			return StringUtils.EMPTY;
		return new BASE64Encoder().encode(src);
	}

	/**
	 * BASE64 解码(String).
	 * 
	 * @param src
	 *            String inputed string
	 * @return String returned string
	 */
	public static final String base64Decode(String src) throws Exception {
		if (StringUtils.isEmpty(src))
			return StringUtils.EMPTY;
		return new String(new BASE64Decoder().decodeBuffer(src));
	}

	/**
	 * BASE64 解码(byte[]).
	 * 
	 * @param src
	 *            String inputed string
	 * @return String returned string
	 */
	public static final byte[] base64DecodeToBytes(String src) throws Exception {
		if (StringUtils.isEmpty(src))
			return ArrayUtils.EMPTY_BYTE_ARRAY;
		return new BASE64Decoder().decodeBuffer(src);
	}

	/**
	 * 对给定字符进行 URL 编码(GB2312).
	 * 
	 * @param src
	 *            String
	 * @return String
	 */
	public static final String urlEncode(String src) throws Exception {
		if (StringUtils.isEmpty(src))
			return StringUtils.EMPTY;
		return URLEncoder.encode(src, GB2312_CHARSET);
	}

	/**
	 * 对给定字符进行 URL 解码(GB2312).
	 * 
	 * @param src
	 *            String
	 * @return String
	 */
	public static final String urlDecode(String src) throws Exception {
		if (StringUtils.isEmpty(src))
			return StringUtils.EMPTY;
		return URLDecoder.decode(src, GB2312_CHARSET);
	}

	/**
	 * 对给定字符进行 URL 编码
	 * 
	 * @param src
	 *            String
	 * @param code
	 *            String 编码
	 * @return String
	 */
	public static final String urlEncode(String src, String code) {
		if (StringUtils.isEmpty(src))
			return StringUtils.EMPTY;
		try {
			return URLEncoder.encode(src, code);
		} catch (UnsupportedEncodingException e) {
			return null;
		}
	}

	/**
	 * 对给定字符进行 URL 解码 报错不抛出，返回null
	 * 
	 * @param src
	 *            String
	 * @param code
	 *            String 编码
	 * @return String
	 */
	public static final String urlDecode(String src, String code) {
		if (StringUtils.isEmpty(src))
			return StringUtils.EMPTY;
		try {
			return URLDecoder.decode(src, code);
		} catch (UnsupportedEncodingException e) {
			return null;
		}
	}

	/**
	 * byte数组=>字符串(加解密时有些特殊情况下getByte不能得到正确结果时使用)
	 * 
	 * 把byte数组转换成非乱码可见的字符串 例如，代表byte数组{0x11,0x12,0x13}的字符串为"111213"
	 * 只能与hex2byte()配套使用，不可与getByte()配套使用
	 * 
	 * @param src
	 *            所要转换的byte数组
	 * @return
	 */
	public static final String byte2hex(byte[] src) {
		if (ArrayUtils.isEmpty(src))
			return StringUtils.EMPTY;
		StringBuilder hs = new StringBuilder();
		for (byte b : src) {
			String stmp = (Integer.toHexString(b & 0XFF));
			if (stmp.length() == 1) {
				hs.append("0").append(stmp);
			} else {
				hs.append(stmp);
			}
		}
		return hs.toString();
	}

	/**
	 * byte数组=>字符串(加解密时有些特殊情况下getByte不能得到正确结果时使用)
	 * 
	 * 把byte数组转换成非乱码可见的字符串 例如，代表byte数组{0x11,0x12,0x13}的字符串为"111213"
	 * 只能与hex2byte()配套使用，不可与getByte()配套使用
	 * 
	 * @param src
	 *            所要转换的byte数组
	 * @return
	 */
	public static final String byte2hex16(byte[] src) {
		if (ArrayUtils.isEmpty(src))
			return StringUtils.EMPTY;
		StringBuilder hs = new StringBuilder();
		for (byte b : src) {
			String stmp = (Integer.toHexString(b & 0XFF));
			if (stmp.length() == 1) {
				hs.append("0").append(stmp);
			} else {
				hs.append(stmp);
			}
		}
		return hs.toString().substring(8, 24);
	}

	/**
	 * 字符串=>byte数组(加解密时有些特殊情况下new String不能得到正确结果时使用)
	 * 
	 * 把代表byte数组的字符串转换成byte数组 例如，字符串为"111213"转换为byte数组{0x11,0x12,0x13}
	 * 只能与byte2hex()配套使用，不能与new String(..)配套使用
	 * 
	 * @param src
	 *            所要转换的字符串
	 * @return
	 * @throws IllegalArgumentException
	 */
	public static final byte[] hex2byte(String src) throws Exception {
		Assert.hasText(src);
		Assert.isTrue(src.length() % 2 == 0,
				"The input string lengh must be an even.");
		char[] arr = src.toCharArray();
		byte[] b = new byte[src.length() / 2];
		for (int i = 0, j = 0, l = src.length(); i < l; i++, j++) {
			StringBuilder swap = new StringBuilder().append(arr[i++]).append(
					arr[i]);
			int byteint = Integer.parseInt(swap.toString(), 16) & 0xFF;
			b[j] = new Integer(byteint).byteValue();
		}
		return b;
	}

	/**
	 * 将一个字串编码成unicode
	 * 
	 * @param src
	 * @return
	 */
	public static final String encode2Unicode(String src) {
		if (StringUtils.isEmpty(src))
			return StringUtils.EMPTY;
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < src.length(); i++) {
			sb.append(encode2Unicode(src.charAt(i)));
		}
		return sb.toString();
	}

	public static String unicodeToString(String str) {
		try {
			Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
			Matcher matcher = pattern.matcher(str);
			char ch;
			while (matcher.find()) {
				ch = (char) Integer.parseInt(matcher.group(2), 16);
				str = str.replace(matcher.group(1), ch + "");
			}
			return str;
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 将一个字符编码成unicode
	 * 
	 * @param c
	 * @return
	 */
	public static final String encode2Unicode(char c) {
		StringBuilder sb = new StringBuilder();
		String tmp;
		if (c > 255) {
			sb.append("\\u");
			// 高8位
			tmp = Integer.toHexString(c >>> 8);
			if (tmp.length() == 1)
				sb.append("0");
			sb.append(tmp);
			// 低8位
			tmp = Integer.toHexString(c & 0xff);
			if (tmp.length() == 1)
				sb.append("0");
			sb.append(tmp);
		} else {
			sb.append(c);
		}
		return sb.toString();
	}

	/**
	 * 将一个字串编码成html所用的某种特殊的unicode
	 * 
	 * @param src
	 * @return
	 */
	public static final String encode2HtmlUnicode(String src) {
		if (StringUtils.isEmpty(src))
			return StringUtils.EMPTY;
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < src.length(); i++) {
			sb.append(encode2HtmlUnicode(src.charAt(i)));
		}
		return sb.toString();
	}

	/**
	 * 将一个字符编码成html所用的某种特殊的unicode
	 * 
	 * @param c
	 * @return
	 */
	public static final String encode2HtmlUnicode(char c) {
		if (c > 255) {
			return new StringBuilder().append("&#").append(c & 0xffff)
					.append(";").toString();
		} else {
			return String.valueOf(c);
		}
	}

	public static final String SUN_PROVIDER = "SunJCE";
	public static final String BOUNCY_CASTLE_PROVIDER = "BC";

	public static final String TRIPPLEDES_ALGORITHM = "DESede";
	public static final String DES_ALGORITHM = "DES";
	public static final String SHA1_ALGORITHM = "SHA-1";
	public static final String MD5_ALGORITHM = "MD5";

	public static final String NORMAL_TRANSFER_TYPE = "DES/CBC/PKCS5Padding";
	public static final String TRIPLEDESPKCS7_TRANSFER_TYPE = "DESede/CBC/PKCS7Padding";

	public static final String DEFAULT_PROVIDER = SUN_PROVIDER;
	public static final String DEFAULT_DES_ALGORITHM = DES_ALGORITHM;
	public static final String DEFAULT_TRANSFER_TYPE = NORMAL_TRANSFER_TYPE;
	public static final String DEFAULT_IV = "0102030405060708";

	public static final String GBK_CHARSET = "GBK";
	public static final String GB2312_CHARSET = "gb2312";
	public static final String UTF8_CHARSET = "utf-8";
	public static final String ISO_CHARSET = "iso-8859-1";

	public static final String DEFAULT_CHARSET = UTF8_CHARSET;

	private CodeUtils() {
	}

	public static String toHexString(String s) {
		String str = "";
		for (int i = 0; i < s.length(); i++) {
			int ch = (int) s.charAt(i);
			String s4 = Integer.toHexString(ch);
			str = str + s4;
		}
		return str;
	}

	// 转化十六进制编码为字符串
	public static String toStringHex(String s) {
		byte[] baKeyword = new byte[s.length() / 2];
		for (int i = 0; i < baKeyword.length; i++) {
			try {
				baKeyword[i] = (byte) (0xff & Integer.parseInt(
						s.substring(i * 2, i * 2 + 2), 16));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		try {
			s = new String(baKeyword, "utf-8");// UTF-16le:Not
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		return s;
	}

	public static void main(String[] args) {
		System.out.println(encode("中文"));
		System.out.println(decode(encode("中文")));
	}

	/*
	 * 16进制数字字符集
	 */
	private static String hexString = "0123456789ABCDEF";

	/*
	 * 将字符串编码成16进制数字,适用于所有字符（包括中文）
	 */
	public static String encode(String str) {
		// 根据默认编码获取字节数组
		byte[] bytes = str.getBytes();
		StringBuilder sb = new StringBuilder(bytes.length * 2);
		// 将字节数组中每个字节拆解成2位16进制整数
		for (int i = 0; i < bytes.length; i++) {
			sb.append(hexString.charAt((bytes[i] & 0xf0) >> 4));
			sb.append(hexString.charAt((bytes[i] & 0x0f) >> 0));
		}
		return sb.toString();
	}

	/*
	 * 将16进制数字解码成字符串,适用于所有字符（包括中文）
	 */
	public static String decode(String bytes) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream(
				bytes.length() / 2);
		// 将每2位16进制整数组装成一个字节
		for (int i = 0; i < bytes.length(); i += 2)
			baos.write((hexString.indexOf(bytes.charAt(i)) << 4 | hexString
					.indexOf(bytes.charAt(i + 1))));
		return new String(baos.toByteArray());
	}

}
